/*
 * 版权所有：杭州领域向尚科技有限公司
 * 地址：浙江省杭州市滨江区滨安路688号天和高科技产业园5-2311
 *
 * (c) Copyright Hangzhou  Area Up Technology Co., Ltd.
 */

package org.elanfox.intellicommunity.common.resource.vfs;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
import org.elanfox.intellicommunity.common.resource.function.FileObjectFunction;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

/**
 * @author allan
 * @date 12/11/2017
 */
@Service
public class VfsHelper {
    private static final Log log = LogFactory.getLog(VfsHelper.class);

    private final FileSystemOptions options = new FileSystemOptions();
    private boolean passive;

    public VfsHelper() {
        super();
        SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);
        SftpFileSystemConfigBuilder.getInstance().setTimeout(options, 30000);

        FtpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);
        FtpFileSystemConfigBuilder.getInstance().setDataTimeout(options, 30000);
        FtpFileSystemConfigBuilder.getInstance().setSoTimeout(options, 30000);
        FtpFileSystemConfigBuilder.getInstance().setPassiveMode(options, passive);
    }

    public <R> R handle(String name, FileObjectFunction<FileObject, R> function) throws IOException {
        StandardFileSystemManager manager = new StandardFileSystemManager();
        manager.init();
        try {
            FileObject file = resolveFile(name, manager);
            try {
                if (function != null) {
                    return function.apply(file);
                }
                return null;
            } catch (FileSystemException ex) {
                togglePassive();
                return function.apply(file);
            } finally {
                file.close();
            }
        } finally {
            manager.close();
        }
    }

    public void saveLocal(String file, InputStream data) throws FileSystemException {
        StandardFileSystemManager manager = new StandardFileSystemManager();
        try {
            manager.init();
            saveLocal(file, data, manager);
        } finally {
            manager.close();
        }
    }

    public void saveLocal(String fileHome, Map<String, InputStream> files) throws FileSystemException {
        StandardFileSystemManager manager = new StandardFileSystemManager();
        manager.init();
        try {
            for (Map.Entry<String, InputStream> entry : files.entrySet()) {
                String file = fileHome + entry.getKey();
                saveLocal(file, entry.getValue(), manager);
            }
        } finally {
            manager.close();
        }
    }

    private void saveLocal(String file, InputStream data, FileSystemManager manager) throws FileSystemException {
        FileObject fileObject = resolveFile(file, manager);
        if (fileObject.exists()) {
            throw new IllegalStateException(file + " already existing");
        }
        OutputStream out = fileObject.getContent().getOutputStream();
        try {
            StreamUtils.copy(data, out);
        } catch (IOException e) {
            throw new FileSystemException(e);
        } finally {
            fileObject.close();
            try {
                data.close();
                out.close();
            } catch (IOException e) {
                log.info("Exception on close stream." + e);
            }
        }
    }

    private void uploadBySftp(String localFile, String sftpUri, FileSystemManager manager) throws FileSystemException {
        // Create local file object
        FileObject localFileObject = manager.resolveFile(localFile);

        // Create remote file object
        FileObject remoteFileObject = manager.resolveFile(sftpUri, options);
        try {
            // Copy local file to sftp server
            remoteFileObject.copyFrom(localFileObject, Selectors.SELECT_SELF);
        } finally {
            localFileObject.close();
            remoteFileObject.close();
        }
    }

    /**
     * 通过sftp批量上传资源
     *
     * @param localFiles
     * @param sftpUri
     * @throws FileSystemException
     */
    public void uploadBySftp(List<String> localFiles, String sftpUri) throws FileSystemException {
        StandardFileSystemManager manager = new StandardFileSystemManager();
        manager.init();
        try {
            for (String localFile : localFiles) {
                uploadBySftp(localFile, sftpUri, manager);
            }
        } finally {
            manager.close();
        }
    }

    /**
     * 通过sftp上传资源
     *
     * @param localFile
     * @param sftpUri
     * @throws FileSystemException
     */
    public void uploadBySftp(String localFile, String sftpUri) throws FileSystemException {
        StandardFileSystemManager manager = new StandardFileSystemManager();
        manager.init();
        try {
            uploadBySftp(localFile, sftpUri, manager);
        } finally {
            manager.close();
        }
    }

    private FileObject resolveFile(String name, FileSystemManager manager) throws FileSystemException {
        return manager.resolveFile(name, options);
    }

    private void togglePassive() {
        passive = !passive;
        FtpFileSystemConfigBuilder.getInstance().setPassiveMode(options, passive);
    }
}
